package gen

import (
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"
	"reflect"
	"server/application/constant/enums"
	"server/application/db/schema/dbops"
	"server/pkg/ifthen"
	"server/setting"
	"strings"
)

// InitGenTableInfo 初始化自动生成代码的实体配置
func InitGenTableInfo() {
	EntityNames := []string{}
	for _, Entity := range setting.EntityGenerationList {
		// 获取实体的类型和值
		entityType := reflect.TypeOf(Entity).Elem()
		EntityNames = append(EntityNames, entityType.Name())
		// 实体的配置信息,先从本地配置中读取
		entityInfo, err := GetEntityInfoByFile(entityType.Name())
		if err != nil {
			entityInfo = &EntityInfo{
				EntityName: entityType.Name(),
			}
		}

		//获取实体自定义配置GenConfig()
		entityInfo.UserConfig = GetEntityUserConfig(Entity)
		//将字段赋值给实体的字段列表
		entityInfo.FieldList = GetFieldList(entityType, entityInfo)

		//将实体配置存入文件
		SaveEntityInfoToFile(entityInfo)

		//生成控制器文件
		GenControllerFile(entityInfo)
		//生成service文件
		GenServiceFile(entityInfo)
		//生成API 常量文件
		GenApiConstPath(entityInfo)
		GenConstTsFile() //生成ts api常量

		//追加路由到路由文件
		//AppendRouteApi(entityInfo)
		GenCommon(entityInfo, GetGoPathRouter(entityInfo), GetPathTemplate(TemplateNameRouterGo), false)

		//根据entityInfo信息配置来创建dto文件
		GenDbDtoFile(entityInfo)

		GenDbDtoTsFile(entityInfo)
		//生成request ts文件
		GenCommon(entityInfo, GetTsPathRequest(entityInfo), GetPathTemplate(TemplateNameRequestTs), false)

		if !entityInfo.UserConfig.OnlyGenBackendCode { //生成前端视图相关的文件
			//生成columns data文件【重要】
			GenColumnsDataTsFile(entityInfo, GetTsPathColumnsDataRule(entityInfo), GetPathTemplate(TemplateNameColumnsEditTs))
			//生成resetList.tsx文件
			GenCommon(entityInfo, GetTsxPathColumnsResetRender(entityInfo), GetPathTemplate(TemplateNameColumnsListResetTsx), false)
			//生成resetCreate.ts
			GenCommon(entityInfo, GetTsPathResetCreateRule(entityInfo), GetPathTemplate(TemplateNameColumnsCreateResetTs), false)
			//生成resetUpdate.ts
			GenCommon(entityInfo, GetTsPathResetUpdateRule(entityInfo), GetPathTemplate(TemplateNameColumnsUpdateResetTs), false)
			//生成 vue 文件
			GenCommon(entityInfo, GetVueIndexPath(entityInfo), GetPathTemplate(TemplateNameIndexVue), false)
			GenCommon(entityInfo, GetVueCreatePath(entityInfo), GetPathTemplate(TemplateNameCreateVue), false)
			GenCommon(entityInfo, GetVueUpdatePath(entityInfo), GetPathTemplate(TemplateNameUpdateVue), false)
		}

	}
	//生成泛型定义文件
	GenGenericsFile(EntityNames)
}

// GetEntityUserConfig 获取用户自定义的实体配置：别名，父级目录等
func GetEntityUserConfig(Entity any) *dbops.EntityConfig {
	m := reflect.ValueOf(Entity).Elem().MethodByName("GenConfig")
	if m.IsValid() {
		result := m.Call(nil)
		val := result[0]
		confType := val.Type().Name()
		if confType == "EntityConfig" {
			fmt.Println("ok")
			confValue := val.Interface().(dbops.EntityConfig)
			return &confValue
		}
	}
	return nil
}

func GenDbDtoTsFile(entityInfo *EntityInfo) {
	var deleteStr, updateStr, infoStr, createStr string
	for _, info := range entityInfo.FieldList {
		isRequire := ""
		if !info.EditConfig.FormRequired {
			isRequire = "?"
		}
		fieldLineItem := fmt.Sprintf("\t%s%s: %s;\n", info.DtoConfig.JsonName, isRequire, toTsTypeByString(info.TypeGo))
		fieldLineArray := fmt.Sprintf("\t%s%s: %s[];\n", info.DtoConfig.JsonName, isRequire, toTsTypeByString(info.TypeGo))

		if info.EditConfig.FormType == enums.FormTypeUploadImages {
			//fieldLineItem = fieldLineArray
		}
		if info.DtoConfig.IsCreate {
			createStr += fieldLineItem
		}
		if info.DtoConfig.IsInfo {
			infoStr += fieldLineItem
		}
		if info.DtoConfig.IsUpdate {
			updateStr += fieldLineItem
		}
		if info.DtoConfig.IsDelete {
			//删除参数默认是批量删除，因此需要加上[]
			deleteStr += fieldLineArray
		}
	}

	dtoOutPath := GetTsPathDto(entityInfo)

	tempDtoPath := GetPathTemplate(TemplateNameDtoTs)
	replaceData := map[string]string{
		PlaceholderEntityTitle:    entityInfo.UserConfig.Title,
		PlaceholderEntityName:     GetRealFileName(entityInfo, NameCameBig),
		PlaceholderEntityLineName: GetRealFileName(entityInfo, NameLine),
		"//{{replace_info}}":      infoStr,
		"//{{replace_create}}":    createStr,
		"//{{replace_update}}":    updateStr,
		"//{{replace_delete}}":    deleteStr,
	}

	err := ReplaceAndWriteTemplate(tempDtoPath, dtoOutPath, replaceData)
	if err != nil {
		log.Println("创建 ts dto文件:" + err.Error())
	}

}

// GenColumnsListTsxFile 生成列 tsx文件
func GenColumnsListTsxFile(entityInfo *EntityInfo, outputPath, templatePath string) {
	//先生成替换掉实体部分内容
	GenCommon(entityInfo, outputPath, templatePath, true)
	//开始构造
	columnsStr := ""
	fieldsStr := ""
	for _, field := range entityInfo.FieldList {
		//如果是info dto则进行构造
		dtoConf := field.DtoConfig
		colConf := field.ColsConfig
		if dtoConf.IsInfo {
			column := fmt.Sprintf(
				`{ prop: "%s", label: "%s",  align: "%s",isShow: %v,tag: %v },`,
				dtoConf.JsonName, field.Label, colConf.Align, colConf.IsShow, colConf.Tag)
			columnsStr += "\t" + column + "\n"
		}
		//构造字段名称 enums
		if dtoConf.IsInfo || dtoConf.IsCreate || dtoConf.IsUpdate || dtoConf.IsDelete {
			fieldsStr += "\t" + fmt.Sprintf(`%s="%s",`, field.DtoConfig.JsonName, field.DtoConfig.JsonName) + "\n"
		}

	}
	replaceData := map[string]string{
		PlaceholderTsxColumns:   columnsStr,
		PlaceholderTsFieldEnums: fieldsStr,
	}
	err := ReplaceAndWriteTemplate(outputPath, outputPath, replaceData)
	if err != nil {
		log.Println("GenColumnsListTsxFile error" + err.Error())
	}
}

// GenColumnsDataTsFile 生成编辑ts文件
func GenColumnsDataTsFile(entityInfo *EntityInfo, outputPath, templatePath string) {
	//先生成替换掉实体部分内容
	GenCommon(entityInfo, outputPath, templatePath, true)
	//开始构造
	columnsStr := ""       //columns list
	columnsUpdateStr := "" //columns list
	columnsCreateStr := "" //columns list
	fieldsStr := ""        //columns list
	importStr := ""        //import语句
	for _, field := range entityInfo.FieldList {
		//如果是info dto则进行构造
		editConf := field.EditConfig
		dtoConf := field.DtoConfig
		colConf := field.ColsConfig
		//构造列表 columns
		if dtoConf.IsInfo {
			column := fmt.Sprintf(
				`{ prop: "%s", label: "%s",  align: "%s",isShow: %v,tag: %v %s},`,
				dtoConf.JsonName, field.Label, colConf.Align, colConf.IsShow, colConf.Tag, GetListColumnsEnumsString(field))
			columnsStr += "\t" + column + "\n"
		}

		//构造字段名称 FieldName enums
		if dtoConf.IsInfo || dtoConf.IsCreate || dtoConf.IsUpdate || dtoConf.IsDelete {
			fieldsStr += "\t" + fmt.Sprintf(`%s="%s",`, field.DtoConfig.JsonName, field.DtoConfig.JsonName) + "\n"
		}

		//构造编辑页 edit columns
		props, tempImportStr := GetDefaultFormProps(editConf.FormType)
		effect := getFormRuleEffect(field) //获取effect
		columnEdit := fmt.Sprintf(
			`{ type: "%s", title: "%s", col:{ span:12 }, field: "%s", effect:%s,props: %s,validate:%s },`,
			getFormType(field), field.Label, dtoConf.JsonName, effect, props, getRequireParam(field))

		importStr += "\t" + tempImportStr + "\n"
		if dtoConf.IsCreate && !field.IsPK { //创建不需要pk字段
			columnsCreateStr += "\t" + columnEdit + "\n"
		}
		if dtoConf.IsUpdate { //编辑表单需要pk字段
			columnsUpdateStr += "\t" + columnEdit + "\n"
		}

	}
	replaceData := map[string]string{
		PlaceholderTsxColumns:      columnsStr,
		PlaceholderTsImportStr:     importStr,
		PlaceholderTsColumnsUpdate: columnsUpdateStr,
		PlaceholderTsColumnsCreate: columnsCreateStr,
		PlaceholderTsFieldEnums:    fieldsStr,
	}
	err := ReplaceAndWriteTemplate(outputPath, outputPath, replaceData)
	if err != nil {
		log.Println("GenColumnsListTsxFile error" + err.Error())
	}
}

// GenDbDtoFile 根据EntityInfo来创建dto文件
func GenDbDtoFile(entityInfo *EntityInfo) {
	EntityName := GetRealFileName(entityInfo, NameCameBig)
	entityLineName := NameToLine(EntityName)
	var deleteStr, updateStr, infoStr, createStr string
	isUseTime := false
	isUseGorm := false
	for _, field := range entityInfo.FieldList {
		omitempty := ""
		if !field.EditConfig.FormRequired {
			omitempty = ",omitempty"
		}
		//Avatar      string    `json:"avatar,omitempty" column:""`\
		//三元运算符
		field.DbFieldName = ifthen.String(field.DbFieldName != "", field.DbFieldName, field.FieldLineName)
		tagFieldName := fmt.Sprintf("column:\"%s\"", field.DbFieldName) //获取字段名

		fieldLineString := fmt.Sprintf("\t%s %s `json:\"%s%s\" %s` \n",
			field.FieldName, field.TypeGo, field.DtoConfig.JsonName, omitempty, tagFieldName)
		// fieldLineArray :=
		if field.EditConfig.FormType == enums.FormTypeUploadImages {
			//fieldLineString=fieldLineArray
		}
		fieldIsUse := false
		if field.DtoConfig.IsDelete {
			fieldIsUse = true
			//删除参数默认是批量因此加上[]
			//多图上传是数组
			deleteStr += fmt.Sprintf("\t%s []%s `json:\"%s%s\" %s` \n",
				field.FieldName, field.TypeGo, field.DtoConfig.JsonName, omitempty, tagFieldName)
		}
		if field.DtoConfig.IsInfo {
			fieldIsUse = true
			infoStr += fieldLineString
		}
		if strings.Contains(fieldLineString, "[]") {
			//表示一对多关联，只有info dto需要
			continue
		}
		if field.DtoConfig.IsCreate {
			fieldIsUse = true
			createStr += fieldLineString
		}
		if field.DtoConfig.IsUpdate {
			fieldIsUse = true
			updateStr += fieldLineString
		}

		//是否使用了gorm库
		if fieldIsUse && strings.Contains(field.TypeGo, "gorm.") {
			isUseGorm = true
		}
		if fieldIsUse && strings.Contains(field.TypeGo, "time.") {
			isUseTime = true
		}
	}

	//import 列表
	importArr := []string{}
	if isUseTime {
		importArr = append(importArr, "time")
	}
	if isUseGorm {
		importArr = append(importArr, "gorm.io/gorm")
	}
	replaceData := map[string]string{
		PlaceholderEntityName:       EntityName,
		PlaceholderEntityLineName:   entityLineName,
		"//{{replace_info}}":        infoStr,
		"//{{replace_create}}":      createStr,
		"//{{replace_update}}":      updateStr,
		"//{{replace_delete}}":      deleteStr,
		"$TABLE_NAME$":              entityInfo.UserConfig.TableName,
		"//{{replace_import_time}}": FormatGoImportStr(importArr), //是否引入time库
	}
	dtoOutPath := GetGoPathDto(entityInfo)
	tempDtoPath := GetPathTemplate(TemplateNameDtoGo)
	err := ReplaceAndWriteTemplate(tempDtoPath, dtoOutPath, replaceData)
	if err != nil {
		log.Println("首次创建dto文件:" + err.Error())
	}
}

// GenCommon 生成默认文件
func GenCommon(entityInfo *EntityInfo, outputPath string, templatePath string, isOverWrite bool) {
	replaceData := map[string]string{
		PlaceholderEntityTitle:         entityInfo.UserConfig.Title,
		PlaceholderEntityName:          GetRealFileName(entityInfo, NameCameBig),
		PlaceholderEntityLineName:      GetRealFileName(entityInfo, NameLine),
		PlaceholderEntityNameCameSmall: GetRealFileName(entityInfo, NameCameSmall),
	}

	//isOverWrite 覆盖现有文件
	if isOverWrite || !FileExists(outputPath) {
		err := ReplaceAndWriteTemplate(templatePath, outputPath, replaceData)
		if err != nil {
			log.Println("生成控文件error:" + outputPath + err.Error())
		}
	}
}

// GenControllerFile 生成控制器文件
func GenControllerFile(entityInfo *EntityInfo) {
	GenCommon(entityInfo, GetGoPathController(entityInfo), GetPathTemplate(TemplateNameController), false)
}

// GenServiceFile 生成service文件
func GenServiceFile(entityInfo *EntityInfo) {
	GenCommon(entityInfo, GetGoPathService(entityInfo), GetPathTemplate(TemplateNameService), false)
}

// AppendRouteApi 向router中追加增删改查路由
func AppendRouteApi(entityInfo *EntityInfo) {
	opts := []string{"Create", "Update", "Delete", "Search"}
	for _, opt := range opts {
		BigName := GetRealFileName(entityInfo, NameCameBig)
		AppendRouterItem(BigName+opt, BigName+opt, "POST")
	}
}

// AppendRouterItem 追加路由，api常量名,控制器名,http类型
func AppendRouterItem(constApiName, controllerName, httpType string) {
	routerFileBytes, err := ioutil.ReadFile(GetGoPathRouterProtect())
	if err != nil {
		log.Println("读取router文件error:" + err.Error())
		return
	}
	if strings.Contains(string(routerFileBytes), constApiName) {
		log.Println(constApiName + "路由已经生成，跳过")
		return
	}
	routeLine := fmt.Sprintf("{Path: apis.Api%s, Handlers: ginp.ConvHandler(api.%s), HttpType: %s},\n\t%s",
		constApiName, controllerName, httpType, AppendStr)

	//开始进行替换
	newContent := strings.ReplaceAll(string(routerFileBytes), AppendStr, routeLine)
	err = ioutil.WriteFile(GetGoPathRouterProtect(), []byte(newContent), 0644)
	if err != nil {
		log.Println("AppendRouterItem error:" + err.Error())
	}
}

// GenApiConstPath 生成api常量文件，增删改查常量
func GenApiConstPath(entityInfo *EntityInfo) {
	GenCommon(entityInfo, GetGoPathApiConst(entityInfo), GetPathTemplate(TemplateNameApis), false)
}

// GenConstTsFile 同步生成ts常量文件
func GenConstTsFile() {
	err := filepath.Walk(GetGoDirConst(), GenTsConstHandler)
	if err != nil {
		panic(err)
	}
}

// GenGenericsFile 生成泛型定义文件
func GenGenericsFile(EntityNames []string) {
	return //不用生成了
	var entityStr, updateStr, infoStr, createStr string
	for _, name := range EntityNames {
		entityStr += fmt.Sprintf(" entity.%s |", name)
		updateStr += fmt.Sprintf(" dbdto.%sUpdate |", name)
		infoStr += fmt.Sprintf(" dbdto.%sInfo |", name)
		createStr += fmt.Sprintf(" dbdto.%sCreate |", name)
	}
	//组装替换文本
	replaceData := map[string]string{
		"//{{replace-entity}}": strings.TrimRight(entityStr, "|"),
		"//{{replace-create}}": strings.TrimRight(createStr, "|"),
		"//{{replace-info}}":   strings.TrimRight(infoStr, "|"),
		"//{{replace-update}}": strings.TrimRight(updateStr, "|"),
	}
	genericsOutPath := GetGoPathGenerics()
	templateNameGenerics := TemplateNameGenerics
	tempControllerPath := GetPathTemplate(templateNameGenerics)
	err := ReplaceAndWriteTemplate(tempControllerPath, genericsOutPath, replaceData)
	if err != nil {
		log.Println("生成泛型定义文件:" + err.Error())
	}

}
